package com.example.springsecurity02loginpage.config;

import com.example.springsecurity02loginpage.exception.LoginFailureHandler;
import com.example.springsecurity02loginpage.exception.LoginSuccessHandler;
import com.example.springsecurity02loginpage.filter.LoginFilter;
import com.example.springsecurity02loginpage.filter.VerificationCodeFilter;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.sql.DataSource;

import java.io.IOException;

import static org.springframework.security.config.Customizer.withDefaults;

@EnableWebSecurity
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // authorizeHttpRequests: 针对http请求进行授权配置
        // login登录页面需要匿名访问
        // permitAll(): 具有所有权限 也就可以匿名访问
        // anyRequest(): 所有请求
        // authenticated(): 已认证用户才可以访问
        http
                .authorizeHttpRequests((authorizeHttpRequests) ->
                        authorizeHttpRequests
                                // 必须admin角色才能访问/admin/api
                                // 必须admin或user角色才能访问/user/api
                                // 任何人都可以访问/app/api
                                // 任何人都可以访问/login
                                //.requestMatchers("/admin/api").hasRole("admin")
                                //.requestMatchers("/user/api").hasAnyRole("admin","user")
                                // 必须admin:api权限才能访问/admin/api
                                .requestMatchers("/admin/api").hasAuthority("admin")
                                // 必须admin:api或user:api权限才能访问/user/api
                                .requestMatchers("/user/api").hasAnyAuthority("admin", "user")
                                .requestMatchers("/app/api").permitAll()
                                .requestMatchers("/captcha/**").permitAll() // 匿名访问 验证码接口
                                .requestMatchers("/login").permitAll()
                                .anyRequest().authenticated()
                );

        // 配置验证码过滤器
        // http.addFilterAfter(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);

        http.exceptionHandling(e -> e.accessDeniedPage("/noAuth"));
        // http后面可以一直点
        // loginPage： 登录页面
        // loginProcessingUrl： 登录接口 过滤器
        // defaultSuccessUrl： 登录成功后跳转到的页面
        http.formLogin(
                formLogin ->
                        formLogin
                                .loginPage("/login").permitAll()
                                .loginProcessingUrl("/login")
                                .failureHandler(new LoginFailureHandler())
                                .successHandler(new LoginSuccessHandler())
                                .defaultSuccessUrl("/index")
        );

          // 配置自定义登录过滤器
        http.addFilterAt(new LoginFilter(), UsernamePasswordAuthenticationFilter.class);

        // 开启跨域
        http.cors(AbstractHttpConfigurer::disable);

        // Customizer.withDefaults(): 关闭CSRF防御
        // 下面3种写法都可以
        // http.csrf(csrf -> csrf.disable());
        // http.csrf(e -> e.disable());
        http.csrf(Customizer.withDefaults());// 跨域漏洞防御：关闭

        // 退出
        http.logout(logout -> logout
                .invalidateHttpSession(true) // 退出后清除session
                .logoutSuccessUrl("/login").permitAll()
        );

        return http.build();
    }

    /*
        @Resource
        private DataSource dataSource;


        @Bean
        public UserDetailsService userDetailsService(){
    //        UserDetails user1 = User.withUsername("admin").password("111111").roles("admin","user").build();
    //        UserDetails user2 = User.withUsername("user").password("111111").roles("user").build();

            JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
            jdbcUserDetailsManager.setDataSource(dataSource);

            UserDetails user1 = User.withUsername("admin").password("111111").authorities("admin:api","user:api").build();
            UserDetails user2 = User.withUsername("user").password("111111").authorities("user:api").build();

            // 在表中创建用户信息
            // 在数据库中创建admin和user用户
            if(!jdbcUserDetailsManager.userExists("admin") && !jdbcUserDetailsManager.userExists("user")){
                jdbcUserDetailsManager.createUser(user1);
                jdbcUserDetailsManager.createUser(user2);
            }


            return jdbcUserDetailsManager;
        }


         */
    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);

        return new ProviderManager(authenticationProvider);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}
